package upgrade

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/influxdata/influxdb/v2"
	"github.com/influxdata/influxdb/v2/cmd/influx/config"
	"github.com/influxdata/influxdb/v2/cmd/internal"
	"github.com/tcnksm/go-input"
	"go.uber.org/zap"
)

func setupAdmin(ctx context.Context, v2 *influxDBv2, req *influxdb.OnboardingRequest) (*influxdb.OnboardingResults, error) {
	res, err := v2.onboardSvc.OnboardInitialUser(ctx, req)

	if err != nil {
		return nil, fmt.Errorf("onboarding error: %w", err)
	}
	return res, nil
}

func isInteractive() bool {
	return !options.force ||
		options.target.userName == "" ||
		options.target.password == "" ||
		options.target.orgName == "" ||
		options.target.bucket == ""
}

func onboardingRequest() (*influxdb.OnboardingRequest, error) {
	if isInteractive() {
		return interactive()
	}
	return nonInteractive()
}

func nonInteractive() (*influxdb.OnboardingRequest, error) {
	if len(options.target.password) < internal.MinPasswordLen {
		return nil, internal.ErrPasswordIsTooShort
	}
	req := &influxdb.OnboardingRequest{
		User:            options.target.userName,
		Password:        options.target.password,
		Token:           options.target.token,
		Org:             options.target.orgName,
		Bucket:          options.target.bucket,
		RetentionPeriod: influxdb.InfiniteRetention,
	}

	dur, err := internal.RawDurationToTimeDuration(options.target.retention)
	if err != nil {
		return nil, err
	}
	if dur > 0 {
		req.RetentionPeriod = dur
	}
	return req, nil
}

func interactive() (req *influxdb.OnboardingRequest, err error) {
	ui := &input.UI{
		Writer: os.Stdout,
		Reader: os.Stdin,
	}
	req = new(influxdb.OnboardingRequest)
	fmt.Println(string(internal.PromptWithColor(`Welcome to InfluxDB 2.0 upgrade!`, internal.ColorYellow)))
	if options.target.userName != "" {
		req.User = options.target.userName
	} else {
		req.User = internal.GetInput(ui, "Please type your primary username", "")
	}
	if options.target.password != "" && len(options.target.password) >= internal.MinPasswordLen {
		req.Password = options.target.password
	} else {
		req.Password = internal.GetPassword(ui, false)
	}
	if options.target.token != "" {
		req.Token = options.target.token
		// else auto-generated by service
	}
	if options.target.orgName != "" {
		req.Org = options.target.orgName
	} else {
		req.Org = internal.GetInput(ui, "Please type your primary organization name", "")
	}
	if options.target.bucket != "" {
		req.Bucket = options.target.bucket
	} else {
		req.Bucket = internal.GetInput(ui, "Please type your primary bucket name", "")
	}

	dur, err := internal.RawDurationToTimeDuration(options.target.retention)
	if err != nil {
		return nil, err
	}

	if dur > 0 {
		req.RetentionPeriod = dur
	} else {
		for {
			rpStr := internal.GetInput(ui, "Please type your retention period in hours.\r\nOr press ENTER for infinite.", strconv.Itoa(influxdb.InfiniteRetention))
			rp, err := strconv.Atoi(rpStr)
			if rp >= 0 && err == nil {
				req.RetentionPeriod = time.Duration(rp) * time.Hour
				break
			}
		}
	}

	if !options.force {
		if confirmed := internal.GetConfirm(ui, func() string {
			rp := "infinite"
			if req.RetentionPeriod > 0 {
				rp = fmt.Sprintf("%d hrs", req.RetentionPeriod/time.Hour)
			}
			return fmt.Sprintf(`
You have entered:
  Username:          %s
  Organization:      %s
  Bucket:            %s
  Retention Period:  %s
`, req.User, req.Org, req.Bucket, rp)
		}); !confirmed {
			return nil, errors.New("setup was canceled")
		}
	}

	return req, nil
}

func saveLocalConfig(sourceOptions *optionsV1, targetOptions *optionsV2, log *zap.Logger) error {
	dPath, dir := targetOptions.cliConfigsPath, filepath.Dir(targetOptions.cliConfigsPath)
	if dPath == "" || dir == "" {
		return errors.New("a valid configurations path must be provided")
	}

	localConfigSVC := config.NewLocalConfigSVC(dPath, dir)
	p := config.DefaultConfig
	p.Token = targetOptions.token
	p.Org = targetOptions.orgName
	if sourceOptions.dbURL != "" {
		p.Host = sourceOptions.dbURL
	}
	if _, err := localConfigSVC.CreateConfig(p); err != nil {
		log.Error("failed to save CLI config", zap.String("path", dPath), zap.Error(err))
		return errors.New("failed to save CLI config")
	}
	log.Info("CLI config has been stored.", zap.String("path", dPath))

	return nil
}
